import logging

import bson
import bson.errors
from fastapi import HTTPException
from fastapi.encoders import jsonable_encoder
from fastapi.exceptions import RequestValidationError
from starlette.requests import Request
from starlette.responses import JSONResponse, Response

__all__ = ["app_exception_handlers"]

from fastapi_template.common.errors import BusinessError
from fastapi_template.webapp.responses import CommonResp


async def _request_info(request: Request) -> str:
    # body = 'no-body'
    # if request.method in ['POST', 'PUT', 'PATCH']:
    #     body = await request.body()
    #     body = body.decode()
    split_char = " | "
    return split_char.join(
        [
            request.method,
            str(request.url),
            # body
        ]
    )


async def http_exception_handler(request: Request, exc: HTTPException) -> Response:
    headers = getattr(exc, "headers", None)
    from fastapi.utils import is_body_allowed_for_status_code

    if not is_body_allowed_for_status_code(exc.status_code):
        return Response(status_code=exc.status_code, headers=headers)
    content = CommonResp.fail(message=exc.detail)
    return JSONResponse(
        status_code=200,
        content=content.model_dump(exclude_none=True),
        headers=headers,
    )


async def bson_exception_handler(request: Request, exc: bson.errors.InvalidId):
    return JSONResponse(
        status_code=200,
        content=CommonResp.fail(message="大圣快收了神通吧").model_dump(
            exclude_none=True
        ),
    )


async def request_validation_exception_handler(
    request: Request, exc: RequestValidationError
) -> JSONResponse:
    errors = []
    for e in exc.errors():
        loc = ".".join((str(e) for e in e["loc"][1:]))
        errors.append(f"{loc} - {e['msg']}")

    error_str = ", ".join(errors)
    content = CommonResp.fail(
        message=f"参数错误：{error_str}", debug=jsonable_encoder(exc.errors())
    )
    return JSONResponse(status_code=200, content=content.model_dump(exclude_none=True))


async def exception_handler(request: Request, exc: Exception) -> JSONResponse:
    request_info = await _request_info(request)
    status_code = 500
    if isinstance(exc, BusinessError):
        content = CommonResp.fail(message=exc.message, code=exc.code)
    else:
        logging.exception("Exception %s", request_info)
        content = CommonResp.fail(message="系统错误", debug=str(exc))
    return JSONResponse(content.model_dump(exclude_none=True), status_code)


app_exception_handlers = {
    HTTPException: http_exception_handler,
    RequestValidationError: request_validation_exception_handler,
    bson.errors.InvalidId: bson_exception_handler,
    BusinessError: exception_handler,
    Exception: exception_handler,
    ValueError: exception_handler,
    # WebSocketRequestValidationError: websocket_request_validation_exception_handler,
    # in case of WebSocket endpoints, ignore it for now
}
